package cn.agiledata.bank.transaction.action;

import java.util.Date;
import java.util.List;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import ocx.AESWithJCE;

import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

import cn.agiledata.bank.account.service.AccountSummary;
import cn.agiledata.bank.account.service.IAccountService;
import cn.agiledata.bank.audit.service.AuditService;
import cn.agiledata.bank.common.dao.AccountTemplateInter;
import cn.agiledata.bank.common.dao.ActionTypeTemplate;
import cn.agiledata.bank.common.dao.TransactionTemplate;
import cn.agiledata.bank.common.service.ICommonService;
import cn.agiledata.bank.common.service.PermissionService;
import cn.agiledata.bank.common.service.email.GainMoneyEmail;
import cn.agiledata.bank.common.util.Constant;
import cn.agiledata.bank.common.util.ForeDict;
import cn.agiledata.bank.common.util.UniqueIdentityUtil;
import cn.agiledata.bank.common.util.WeatherUtil;
import cn.agiledata.bank.hibernateBean.Account;
import cn.agiledata.bank.hibernateBean.AccountLimit;
import cn.agiledata.bank.hibernateBean.ActionType;
import cn.agiledata.bank.hibernateBean.Groups;
import cn.agiledata.bank.hibernateBean.Operator;
import cn.agiledata.bank.hibernateBean.SubAccount;
import cn.agiledata.bank.hibernateBean.Transaction;
import cn.agiledata.bank.myspace.bean.PeerBankAccountBean;
import cn.agiledata.bank.myspace.service.AuthenticateService;
import cn.agiledata.bank.myspace.service.IMySpaceService;
import cn.agiledata.bank.myspace.service.PeerAccountService;
import cn.agiledata.bank.transaction.audit.service.EsPrePayTransService;
import cn.agiledata.bank.transaction.exception.TransactionException;
import cn.agiledata.bank.transaction.form.DrawForm;
import cn.agiledata.bank.transaction.form.DrawToOtherForm;
import cn.agiledata.bank.transaction.form.MultiStepForm;
import cn.agiledata.bank.transaction.service.FeeRatioInterf;
import cn.agiledata.bank.transaction.service.TransactionService;
import com.vnv.infrastructure.action.IPBBaseAction;
import com.vnv.infrastructure.logic.IPBAppFactory;
import com.vnv.infrastructure.logic.IPBMgrFactory;

/**
 * 提现Action
 * 
 * @author maxianbo
 * 
 */
public abstract class DrawAction extends IPBBaseAction {
    /**
     * 分发调度
     */
    public final ActionForward assign(ActionMapping mapping,
            ActionForm actionForm, HttpServletRequest request,
            HttpServletResponse response) throws Exception {
        MultiStepForm form = (MultiStepForm) actionForm;
        int step = form.getStep();
        ActionForward forward;
        switch (step) {
        case 0: // 第0步准备初始化数据
            forward = prepare(mapping, actionForm, request, response);
            break;
        case 1: // 第1步填写提现信息
            forward = fill(mapping, actionForm, request, response);
            break;
        case 2: // 第2步进行提现操作
            forward = draw(mapping, actionForm, request, response);
            break;
        default: // 其它步骤序数跳转到第0步，进入新的充值流程，防止错误的步骤序数被提交
            form.setStep(0);
            forward = prepare(mapping, actionForm, request, response);
        }
        // 设置新的步骤序数，最大步骤序数为2
        form.setNextStep(2);
        return forward;
    }

    /**
     * 准备初始化数据
     */
    public ActionForward prepare(ActionMapping mapping, ActionForm actionForm,
            HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        // 判断当前用户是否具有转出（即提现）或付款权限
        if (!PermissionService.getPermissionService().hasPermission(request,
                getPermitType())) {
            handleAppException(mapping, actionForm, request, response,
                    TransactionException.NO_PERMISSION);
        }

        loadSubAccountList(request);
        // 默认选择默认子账户
        DrawForm form = (DrawForm) actionForm;
        if (form.getSubAccountId() == 0) {
            Account account = getCurrentAccount(request);
            SubAccount defaultSubAccount = account.getDefaultSubAccount();
            form.setSubAccountId(defaultSubAccount.getId().longValue());
        }
        loadFeeRatioMode(request);
        // 设置交易类型
        form.setActionType(getActionType());
        return mapping.findForward(ForeDict.FORWARD_STEP_1);
    }

    /**
     * 设置费率收取方式
     */
    private void loadFeeRatioMode(HttpServletRequest request) {
        Groups group = getMySpaceService().getGroupByAccount(
                getCurrentAccount(request), Constant.GROUP_TYPE_A);
        String feeRatioMode = getFeeRatioService().getFeeRatio(group,
                getActionType());
        request.setAttribute("feeRatioMode", feeRatioMode);
    }

    /**
     * 填写提现信息
     */
    public ActionForward fill(ActionMapping mapping, ActionForm actionForm,
            HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        DrawForm form = (DrawForm) actionForm;
        try {
            // 判断当前用户是否具有转出（即提现）或付款权限
            if (!PermissionService.getPermissionService().hasPermission(
                    request, getPermitType())) {
                throw TransactionException.NO_PERMISSION;
            }

            String subAccountId = String.valueOf(form.getSubAccountId());
            SubAccount subAccount = getAccountTemplate().getSubAccount(
            		subAccountId);
            Account curAccount = getCurrentAccount(request);
            Account account = subAccount.getAccount();
            if (!curAccount.equals(account)) {
            	throw TransactionException.NOT_THE_SAME_ACCOUNT;
            }
            Double amount = validateAmount(form.getAmount());
            validateSubAccountBalance(subAccountId, amount);
            
            request.setAttribute("subAccount", subAccount);
            
            if(Constant.ACTION_TYPE_D.equals(form.getActionType())) {//只有提现才有金额限制
            	validateAccountLimit(account, amount.doubleValue());
            }
            // 获取费率
            Groups group = getMySpaceService().getGroupByAccount(account,
                    Constant.GROUP_TYPE_A);
            double debitFee=0.0;
            if(group.getId()!=null)
            {
            	debitFee = getFeeRatioService().getDebitFee(subAccount,
                        getActionType(), amount, group.getId().toString());
            }
            request.setAttribute("fee", new Double(debitFee));
            if (getCommonService().compareAmounts(
                    subAccount.getId().toString(), form.getActionType(), amount) != 0) {
                // 生成充值金额低于费率的异常
                throw TransactionException.getAmountUnderFeeException(amount
                        .doubleValue(), debitFee);
            }
        } catch (TransactionException e) {
            // 处理业务逻辑异常
            handleAppException(mapping, actionForm, request, response, e);
            // 跳转到第1步，之前重新装载子账户列表
            loadSubAccountList(request);
            // 设置费率收取方式
            loadFeeRatioMode(request);
            // 设置为上一步的步骤序数，防止序数与步骤不同步
            form.setPreviousStep();
            return mapping.findForward(ForeDict.FORWARD_STEP_1);
        }
        // 判断是否个人用户
        Boolean personAccount = new Boolean(isPersonAccount(request));
        request.setAttribute("personAccount", personAccount);

        saveToken(request);
        return mapping.findForward(ForeDict.FORWARD_STEP_2);
    }

    /**
     * 进行提现操作
     */
    public synchronized ActionForward draw(ActionMapping mapping,
            ActionForm actionForm, HttpServletRequest request,
            HttpServletResponse response) throws Exception {
        DrawForm form = (DrawForm) actionForm;
        String subAccountId = String.valueOf(form.getSubAccountId());
        SubAccount subAccount = getAccountTemplate()
                .getSubAccount(subAccountId);
        try {
            if (!isTokenValid(request)) {
                throw TransactionException.REPEATEDLY_SUBMIT;
            }
            Account curAccount = getCurrentAccount(request);
            Account account = subAccount.getAccount();
            if (!curAccount.equals(account)) {
            	throw TransactionException.NOT_THE_SAME_ACCOUNT;
            }
            Double amount = validateAmount(form.getAmount());
            validateSubAccountBalance(subAccountId, amount);
            if(Constant.ACTION_TYPE_D.equals(form.getActionType())) {//只有提现才有金额限制
            	validateAccountLimit(account, amount.doubleValue());
            }
            HttpSession session = request.getSession();
    		String mcrypt_key_1=(String)session.getAttribute("mcrypt_key");
    		session.removeAttribute("mcrypt_key");//清除session
    		String password1 = form.getPassword(); // 密码
    		String password=AESWithJCE.getResult(mcrypt_key_1,password1);
    		
            try {
                validatePassword(request, password);
            } catch (TransactionException e) {
                if (e.getCode().equals(
                        "error.transaction.pay_password_error_logout")) {// 向session设置错误消息，且强制登出
                    request.getSession().setAttribute("payPasswordErrorMsg",
                            getAppExceptionMessage(request, e));
                    return mapping.findForward("logout");
                } else {
                    throw e;
                }
            }
            Transaction trans = createTransaction(form);
            getTransactionService().validateTransactable(
                    trans.getDebit().getAccount(),
                    trans.getCredit().getAccount(), trans.getPersonName(),
                    getActionType());
            request.setAttribute("transaction", trans);
            try {
                if (isPersonAccount(account)) { // 个人用户
                    // 保存交易，并更新账户金额限制信息
                    getTransactionTemplate().saveTransactionWithAccountLimit(
                            trans, account);
                    /**
                     * @todo 邮件提醒，重构至保存交易的底层代码中
                     */
                    TransactionTemplate template = getTransactionTemplate();
                    GainMoneyEmail mail = (GainMoneyEmail) IPBAppFactory
                            .getObj("gainMoneyMail");
                    String actionType = getActionType();
                    if (template.ifSendMail(account, Constant.GAINMONEY)) {
                        if (Constant.ACTION_TYPE_G.equals(actionType)) {
                            mail.setEmailTitle("您的账户直付通知");
                            mail.setContentTitle("账户直付");
                            mail.setPaytype("账户直付");
                        } else {
                            mail.setEmailTitle("您的账户提现通知");
                            mail.setContentTitle("提现");
                            mail.setPaytype("提现");
                            // mail.setMes("提现银行卡尾号为" + cardNum);
                        }
                        mail.sendMail(trans, null);
                    }
                    if (template.ifSendMobile(account, Constant.GAINMONEY)) {
                        if (Constant.ACTION_TYPE_G.equals(actionType)) {
                            mail.setPaytype("账户直付");
                            mail.setSmsContext("sms-drawToOther.vm");
                        } else {
                            mail.setPaytype("提现");
                        }
                        mail.sendSms(trans, null);
                    }

                    // 应该在此处把交易对方添加到预设银行账户列表里，但如果当前用户是个人，则写进数据库时出错，故移到上面。
                } else { // 企业用户
                    AccountSummary accountsummary = WeatherUtil
                            .getLoingInfoFromSession(request);
                    String operatorId = accountsummary.getOperatorCode() == null ? null
                            : accountsummary.getOperatorCode().toString();
                    Operator operator = operatorId == null ? null
                            : accountsummary
                                    .getOperatorByIdInstance(operatorId);
                    trans.setOperatorSeq(operatorId);
                    getEsPrePayTransService().createEsPrePayTrans(trans,
                            account, operator);
                }
            } catch (RuntimeException e) {
                throw TransactionException.TRANSACTION_FAILED;
            }
            resetToken(request);
        } catch (TransactionException e) {
            // 处理业务逻辑异常
            handleAppException(mapping, actionForm, request, response, e);
            // 跳转到第2步，之前重新装载部分数据
            request.setAttribute("subAccount", subAccount);
            // 判断是否个人用户
            Boolean personAccount = new Boolean(isPersonAccount(request));
            request.setAttribute("personAccount", personAccount);
            // 设置为上一步的步骤序数，防止序数与步骤不同步
            form.setPreviousStep();
            return mapping.findForward(ForeDict.FORWARD_STEP_2);
        }

        // 判断是否个人用户
        Boolean personAccount = new Boolean(isPersonAccount(request));
        request.setAttribute("personAccount", personAccount);

        return mapping.findForward(ForeDict.FORWARD_STEP_3);
    }

    /**
     * 
     * @return 交易类型
     */
    protected abstract String getActionType();

    /**
     * 
     * @return 权限类型
     */
    protected abstract String getPermitType();

    /**
     * 根据交易类型值获取交易类型对象
     */
    protected final ActionType getActionType(String actionTypeValue) {
        long actionTypeId = Long.parseLong(actionTypeValue);
        return getActionTypeTemplate().getActionTypeById(actionTypeId);
    }

    protected final ActionTypeTemplate getActionTypeTemplate() {
        return (ActionTypeTemplate) IPBAppFactory
                .getObj(ForeDict.ACTION_TYPE_TEMPLATE);
    }

    protected final IMySpaceService getMySpaceService() {
        return (IMySpaceService) IPBAppFactory
                .getObj(ForeDict.SERVICE_MY_SPACE);
    }

    protected final AccountTemplateInter getAccountTemplate() {
        return (AccountTemplateInter) IPBAppFactory
                .getObj(ForeDict.ACCOUNT_TEMPLATE);
    }

    protected final AuthenticateService getAuthenticateService() {
        return (AuthenticateService) IPBAppFactory
                .getObj(ForeDict.SERVICE_AUTHENTICATE);
    }

    protected final FeeRatioInterf getFeeRatioService() {
        return (FeeRatioInterf) IPBMgrFactory
                .getMgr(ForeDict.SERVICE_FEE_RATIO);
    }

    protected final AuditService getAuditService() {
        return (AuditService) IPBAppFactory.getObj(ForeDict.SERVICE_AUDIT);
    }

    protected final IAccountService getAccountService() {
        return (IAccountService) IPBAppFactory.getObj(ForeDict.SERVICE_ACCOUNT);
    }

    protected final ICommonService getCommonService() {
        return (ICommonService) IPBAppFactory.getObj(ForeDict.SERVICE_COMMON);
    }

    protected final TransactionTemplate getTransactionTemplate() {
        return (TransactionTemplate) IPBAppFactory
                .getObj(ForeDict.TRANSACTION_TEMPLATE);
    }

    protected final EsPrePayTransService getEsPrePayTransService() {
        return (EsPrePayTransService) IPBAppFactory
                .getObj(ForeDict.SERVICE_ES_PRE_PAY_TRANS);
    }

    protected final TransactionService getTransactionService() {
        return (TransactionService) IPBAppFactory
                .getObj(ForeDict.SERVICE_TRANSACTION);
    }

    /**
     * 获取当前账户
     */
    protected final Account getCurrentAccount(HttpServletRequest request) {
        long accountId = WeatherUtil.getAccountIdFromSession(request);
        return getAccountTemplate().getAccount(String.valueOf(accountId));
    }

    /**
     * 加载子账户列表
     */
    private void loadSubAccountList(HttpServletRequest request) {
        Account account = getCurrentAccount(request);
        SubAccount defaultSubAccount = account.getDefaultSubAccount();
        if (isPersonAccount(account)) { // 个人用户
            // 获取当前个人用户的所有子账户列表
            Set subAccounts = account.getSubAccounts();
            request.setAttribute("subAccounts", subAccounts);
        } else { // 企业操作员
            // 获取当前企业操作员具有提现权限的子账户列表
            List subAccounts = PermissionService.getPermissionService()
                    .getSubAccounts(request, Constant.PERMIS_TYPE_GAINMONEY,
                            defaultSubAccount);
            request.setAttribute("subAccounts", subAccounts);
        }
    }

    /**
     * 校验金额的格式和数值
     * 
     * @param amountValue
     *            字符串型金额值
     * @return Double型金额值
     * @throws TransactionException
     *             如果金额格式错误或金额值小于等于0
     */
    protected Double validateAmount(String amountValue)
            throws TransactionException {
        try {
            Double amount = new Double(amountValue);
            if (amount.doubleValue() <= 0) {
                throw TransactionException.INVALID_AMOUT;
            }
            return amount;
        } catch (NumberFormatException e) {
            throw TransactionException.INVALID_AMOUT;
        }
    }

    /**
     * 判断当前用户是否个人用户
     */
    protected final boolean isPersonAccount(HttpServletRequest request) {
        return isPersonAccount(getCurrentAccount(request));
    }

    /**
     * 判断指定用户是否个人用户
     */
    protected final boolean isPersonAccount(Account account) {
        return Constant.ACCOUNT_TYPE_BUYER.equals(account.getAccountType());
    }

    /**
     * 根据提现数据创建交易对象
     */
    protected Transaction createTransaction(DrawForm drawForm) {
        // 新建交易
        Transaction trans = new Transaction();
        AccountTemplateInter at = getAccountTemplate();
        // 付款方：提现子账户
        String subAccountId = String.valueOf((drawForm.getSubAccountId()));
        SubAccount debitSubAccount = at.getSubAccount(subAccountId);
        trans.setDebit(debitSubAccount);
        // 收款方：内部提现账户
        trans.setCredit(at.getRemitAccount());
        // 交易状态：交易成功
        trans.setTransStatus(Constant.TRANS_STATUS_TRANSACTIONSUCCESS);
        // 生成交易编号
        trans.setTransId(UniqueIdentityUtil.getTransactionSequence());
        // 交易类型
        trans.setActionType(getActionType(drawForm.getActionType()));
        // 非担保交易
        trans.setTradeType(Constant.TRADE_TYPE_NOASSURE);
        // 充值金额
        trans.setAmount(new Double(drawForm.getAmount()));
        // 设置记账模型
        trans.setOperate(Constant.MODEL_TYPE_GAINMONEY);
        // 费率
        double debitFee = getFeeRatioService().getDebitFee(trans);
        trans.setDebitFee(new Double(debitFee));
        // 封装插入日期和修改日期
        WeatherUtil.fillCreateAndUpdateInfo(trans, debitSubAccount.getAccount()
                .getId().toString());
        // 交易开始时间
        trans.setBeginDate(new Date());
        // 交易截止时间
        trans.setEndDate(new Date());
        // 用途
        trans.setPurpose(drawForm.getPurpose());
        // 备注
        trans.setRemark(drawForm.getRemark());
        // 内部状态：进行实际转账操作
        trans.setInsideStatus(Constant.INSIDE_STATUS_YES);
        return trans;
    }

    /**
     * 个人用户校验支付密码
     */
    private void validatePassword(HttpServletRequest request, String password)
            throws TransactionException {
        Account account = getCurrentAccount(request);
        if (isPersonAccount(account)
                && !getAccountTemplate().compareAccountPickUpPassword(account,
                        password)) {
            // throw TransactionException.PAY_PASSWORD_ERROR;

            IAccountService accountService = (IAccountService) IPBAppFactory
                    .getObj(ForeDict.SERVICE_ACCOUNT);

            // 记入系统日志
            accountService.writeSystemLogWhenPayPasswordError(account, request
                    .getRemoteAddr());

            // 获取账户当日支付密码输错次数
            int currentPayPwdErr = accountService
                    .getCurrentPayPwdErrByAccountId(account.getId());

            // 获取系统允许的当日支付密码累计输错次数上限(大于这个值就锁定该账户，明日才能登录)
            int maxPayPwdErr = accountService.getMaxPayPwdErrFromSysBook();

            if (currentPayPwdErr <= maxPayPwdErr) {// 支付密码输入错误，且当日累计输错次数小于或等于系统所允许的上限值--->抛异常
                throw TransactionException.getPayPasswordError(String
                        .valueOf(currentPayPwdErr), String
                        .valueOf(maxPayPwdErr));
            } else {// 支付密码输入错误，且当日累计输错次数大于系统所允许的上限值--->锁定账户，抛异常
                String sign = account.getEmail() == null ? account.getMobile()
                        : account.getEmail();
                accountService.lockAccountLoginBySign(sign);

                throw TransactionException.getPayPasswordErrorNeedLogout(String
                        .valueOf(maxPayPwdErr));
            }
        }
    }

    /**
     * 校验子账户余额
     */
    private void validateSubAccountBalance(String subAccountId, Double amount)
            throws TransactionException {
        if (getAccountTemplate().compareAccountMoney(subAccountId, amount) != 0) {
            throw TransactionException.LACK_OF_BALANCE;
        }
    }

    /**
     * 校验账户金额限制
     * 
     * @throws TransactionException
     */
    private void validateAccountLimit(Account account, double amount)
            throws TransactionException {
        if (isPersonAccount(account)) { // 个人用户才进行金额限制
            TransactionTemplate transactionTemplate = getTransactionTemplate();
            AccountLimit accountLimit = transactionTemplate
                    .getAcctlimit(account);
            if (transactionTemplate.ifHigher(account, amount, accountLimit)) {
                throw TransactionException.getAccountLimitException(account,
                        accountLimit, amount);
            }
        }
    }

    /**
     * 保存交易对方记录
     */
    protected void savePeerBankAccount(HttpServletRequest request,
            DrawToOtherForm form) {
        PeerBankAccountBean bean = new PeerBankAccountBean();

        bean.setAccountId(getCurrentAccount(request).getId());

        bean.setPeerName(form.getCreditName());
        bean.setBankSeq(String.valueOf(form.getCreditBankId()));
        bean.setProvSeq(form.getCreditProvinceId());
        bean.setCitySeq(form.getCreditCityId());
        bean.setBankName(form.getCreditBankName());
        bean.setCardNo(form.getCreditAccountNo());

        bean.setPeerEmail(form.getCreditEmail());
        bean.setPeerPhone(form.getCreditFixedTel());
        bean.setPeerMobile(form.getCreditMobile());

        PeerAccountService service = (PeerAccountService) IPBAppFactory
                .getObj("peerAccountService");
        int returnValue = service.peerBankAccountAddBeforeDrawToOther(bean);

        if (returnValue == 0) {
            System.out
                    .println("############# peerBankAccountAddBeforeDrawToOther success.");
        } else if (returnValue == 1) {
            System.out
                    .println("############# peerBankAccountAddBeforeDrawToOther failed(throw exception). ");
        } else {
            System.out
                    .println("############# peerBankAccountAddBeforeDrawToOther failed(duplicated cardNo).");
        }

    }
}